#ifndef _INTERFACE_HPP
#define _INTERFACE_HPP

#include <time.h>
#include <signal.h>
#include <pthread.h>
#include <stdint.h>

#include <string>
#include <vector>

void print_resp(std::vector<uint8_t> resp);
void tokenize(const std::string& str,  const std::string& delimiters,
        std::vector<std::string>& tokens);


/* base packet protocol structure defined by revotech */
typedef struct packet_s {
    uint8_t s_code;
    uint8_t addr;
    uint8_t size;
    uint8_t data[0];
} b_packet_t;

/* panel packet protocol structure defined by revotech */
typedef struct panel_packet_s {
    uint8_t s_code;
    uint8_t addr;
    uint8_t size;
    uint8_t cmd;

    /* 16 sensors packed in two bytes */
    uint8_t s16 : 1;
    uint8_t s15 : 1;
    uint8_t s14 : 1;
    uint8_t s13 : 1;
    uint8_t s12 : 1;
    uint8_t s11 : 1;
    uint8_t s10 : 1;
    uint8_t s9  : 1;
    uint8_t s8  : 1;
    uint8_t s7  : 1;
    uint8_t s6  : 1;
    uint8_t s5  : 1;
    uint8_t s4  : 1;
    uint8_t s3  : 1;
    uint8_t s2  : 1;
    uint8_t s1  : 1;

    /* 12 buttons packed in tree bytes */
    uint8_t b12 : 2;
    uint8_t b11 : 2;
    uint8_t b10 : 2;
    uint8_t b9  : 2;
    uint8_t b8  : 2;
    uint8_t b7  : 2;
    uint8_t b6  : 2;
    uint8_t b5  : 2;
    uint8_t b4  : 2;
    uint8_t b3  : 2;
    uint8_t b2  : 2;
    uint8_t b1  : 2;

    /* dynamically allocated array for the digital sensors and csum */
    uint8_t data[0];
} p_packet_t;

enum {
    SAMPLE,
    CONDITIONAL
};

struct Commands_info {
    bool type;

    std::string expr;
    std::vector< std::pair<std::string, std::string> > if_true;
    std::vector< std::pair<std::string, std::string> > if_false;
    std::vector< std::pair<std::string, std::string> > sample;
};

struct Serial_info{
    int s_fd;
    pthread_mutex_t s_mutex;
};

class Base;
class Mode;
class Panel;
class Sensor;
class Command;
class Condition;

class Base {
    public:
        std::string gid;
        std::string name;
        std::string value;

        pthread_mutex_t set_get_mutex;

        Base() { pthread_mutex_init(&this->set_get_mutex, NULL); }

        virtual std::string get();
        virtual void set(std::string v);
};

class Sensor : public Base {
    public:
        uint8_t id;
        float s_value;
        std::string type;

        std::string get();
        void set(float v);
        void set(std::string v);
};

class Panel : public Base {
    public:
        std::string port;
        std::string init_data;
        std::string update_data;

        std::vector<std::string> s_gids;

        timer_t timerid;
        struct sigevent sevp;
        struct itimerspec timeout;

        pthread_t thread;
        
        Panel();
        void init();
        void wait();
        void update();
        void start_timer();

        static void* update_routine(void* obj);

        std::string get();
        void set(std::string v);
};

class Device : public Base {
    public:
        std::string port;
        std::string data_for_set;
        std::string data_for_get;

        size_t start_byte;
        size_t byte_count;

        std::string get();
        void set(std::string v);
};

class Command : public Base {
    public:
        std::string port;
        std::string uniq;
        std::string data_for_set;
        std::string data_for_get;

        void init();
        std::string get();
        void set(std::string v);
};

class Condition {
    public:
        Mode *m;
        std::string expr;

        Commands_info true_cmds;
        Commands_info false_cmds;

        pthread_t thread;
        pthread_mutex_t mutex;

        timer_t timerid;
        struct sigevent sevp;
        struct itimerspec timeout;
        
        timer_t step_timerid;
        struct sigevent step_sevp;
        struct itimerspec step_timeout;

        Condition();

		void stop();
		void wait();
		void start();

        bool is_timeout(timer_t &timerid);
        void start_timer(std::string t_str);
        static void* start_routine(void* obj);
};

class Mode : public Base {
    public:
        std::vector<Condition> cnds;
        std::vector< std::pair<std::string, std::string> > actions;

        std::string state;
        int working_count;

        pthread_mutex_t v_mutex;
        pthread_mutex_t working_mutex;

        Mode() 
        { 
            pthread_mutex_init(&this->set_get_mutex, NULL);
            pthread_mutex_init(&this->v_mutex, NULL);
            pthread_mutex_init(&this->working_mutex, NULL);
        }

        void stop();
        void start();
        void wait();

        void set(std::string v);
        std::string get();

        void set_value(std::string v);
        std::string get_value();
};

class Timer : public Base{
    public:
        std::string state;
        std::vector< std::pair<std::string, std::string> > actions;

        pthread_mutex_t v_mutex;

        timer_t timerid;
        struct sigevent sevp;
        struct itimerspec timeout;

        pthread_t thread;
        pthread_mutex_t t_mutex;

        Timer() 
        { 
            pthread_mutex_init(&this->set_get_mutex, NULL);
            pthread_mutex_init(&this->v_mutex, NULL);
            pthread_mutex_init(&this->t_mutex, NULL);

            this->sevp.sigev_notify = SIGEV_NONE;
            timer_create(CLOCK_REALTIME, &this->sevp, &this->timerid);
        }

        void stop();
        void start();

        void set(std::string v);
        std::string get();

        void set_value(std::string v);
        std::string get_value();

        bool is_timeout();
        void start_timer();
        static void* start_routine(void* obj);

};

class Gsm : public Base {
    public:
        std::string number;
        std::string text;

        pthread_mutex_t v_mutex;

        timer_t timerid;
        struct sigevent sevp;
        struct itimerspec timeout;

        pthread_t thread;
        pthread_mutex_t t_mutex;

        std::string get();
        void set(std::string v);
        void send_msg();
};

#endif // INTERFACE_HPP
